package live

import (
	"encoding/json"
	"fmt"
	"io"
	"live_record_app/library/ffmpeg"
	u "live_record_app/utils"
	"net/http"
	"regexp"
	"strconv"
	"time"

	"github.com/Akegarasu/blivedm-go/client"
	"github.com/Akegarasu/blivedm-go/message"
	"github.com/tidwall/gjson"
)

var log = u.LoadLogs("logs/live", "live.log")
var liveHost = "https://api.live.bilibili.com"
var host = "https://api.bilibili.com"

type roomInfo struct {
	Uid              int    `json:"uid"`
	Room_id          int    `json:"room_id"`
	Short_id         int    `json:"short_id"`
	Attention        int    `json:"attention"`
	Online           int    `json:"online"`
	Is_portrait      bool   `json:"is_portrait"`
	Description      string `json:"description"`
	Live_status      int    `json:"live_status"`
	Area_id          int    `json:"area_id"`
	Parent_area_id   int    `json:"parent_area_id"`
	Parent_area_name string `json:"parent_area_name"`
	Old_area_id      int    `json:"old_area_id"`
	Background       string `json:"background"`
	Title            string `json:"title"`
	User_cover       string `json:"user_cover"`
	Keyframe         string `json:"keyframe`
	Live_time        string `json:"live_time"`
	Tags             string `json:"tags"`
}

type getRoomInfo struct {
	Code    int      `json:"code"`
	Message string   `json:"message"`
	Msg     string   `json:"msg"`
	Data    roomInfo `json:"data"`
}

type medal interface{}

type fans_medal struct {
	Show  bool  `json:"show"`
	Wear  bool  `json:"wear"`
	Medal medal `json:"medal"`
}
type official struct {
	Role  bool   `json:"role"`
	Title string `json:"title"`
	Desc  string `json:"desc"`
	Type  int    `json:"Type"`
}
type label struct {
	Path                      string `json:"path"`
	Text                      string `json:"text"`
	Label_theme               string `json:"label_theme"`
	Text_color                string `json:"text_color"`
	Bg_style                  int    `json:"bg_style"`
	Bg_color                  string `json:"bg_color"`
	Border_color              string `json:"border_color"`
	Use_img_label             string `json:"use_img_label"`
	Img_label_uri_hans        string `json:"img_label_uri_hans"`
	Img_label_uri_hant        string `json:"img_label_uri_hant"`
	Img_label_uri_hans_static string `json:"img_label_uri_hans_static"`
	Img_label_uri_hant_static string `json:"img_label_uri_hant_static"`
}
type vip struct {
	Type                 int    `json:"Type"`
	Status               int    `json:"status"`
	Due_date             int    `json:"due_date"`
	Vip_pay_type         int    `json:"vip_pay_type"`
	Theme_type           int    `json:"theme_type"`
	Label                label  `json:"label"`
	Avatar_subscript     int    `json:"avatar_subscript"`
	Nickname_color       string `json:"nickname_color"`
	Role                 int    `json:"role"`
	Avatar_subscript_url string `json:"avatar_subscript_url"`
	Tv_vip_status        int    `json:"tv_vip_status"`
	Tv_vip_pay_type      int    `json:"tv_vip_pay_type"`
}
type pendant struct {
	Pid                 int    `json:"pid"`
	Name                string `json:"name"`
	Image               string `json:"image"`
	Expire              int    `json:"expire"`
	Image_enhance       string `json:"image_enhance"`
	Image_enhance_frame string `json:"image_enhance_frame"`
}
type nameplate struct {
	Nid         int    `json:"nid"`
	Name        string `json:"name"`
	Image       string `json:"image"`
	Image_small string `json:"image_small"`
	Level       string `json:"level"`
	Condition   string `json:"condition"`
}
type user_honour_info struct {
	Mid    int         `json:"mid"`
	Colour string      `json:"colour"`
	Tags   interface{} `json:"tags"`
}
type theme struct {
}
type sys_notice struct {
}
type watched_show struct {
	Switch        string `json:"switch"`
	Num           int    `json:"num"`
	Text_small    string `json:"text_small"`
	Text_large    string `json:"text_large"`
	Icon          string `json:"icon"`
	Icon_location string `json:"icon_location"`
	Icon_web      string `json:"icon_web"`
}
type live_room struct {
	RoomStatus     int          `json:"roomStatus"`
	LiveStatus     int          `json:"liveStatus"`
	Url            string       `json:"url"`
	Title          string       `json:"title"`
	Cover          string       `json:"cover"`
	Roomid         int          `json:"roomid"`
	RoundStatus    int          `json:"roundStatus"`
	Broadcast_type int          `json:"broadcast_type"`
	Watched_show   watched_show `json:"watched_show"`
}
type profession struct {
	Name       string `json:"name"`
	Department string `json:"department"`
	Title      string `json:"title"`
	Is_show    int    `json:"is_show"`
}
type series struct {
	User_upgrade_status int  `json:"user_upgrade_status"`
	Show_upgrade_window bool `json:"show_upgrade_window"`
}
type show_info struct {
	Show     bool   `json:"show"`
	State    int    `json:"state"`
	Title    string `json:"title"`
	Icon     string `json:"icon"`
	Jump_url string `json:"jump_url"`
}
type elec struct {
	Show_info string `json:"show_info"`
}
type school struct {
}
type tags struct {
}
type mcn_info struct {
}
type gaia_data struct {
}
type contract struct {
}
type userInfo struct {
	Mid              int              `json:"mid"`
	Name             string           `json:"name"`
	Sex              string           `json:"sex"`
	Face             string           `json:"face"`
	Face_nft         int              `json:"face_nft"`
	Face_nft_type    int              `json:"face_nft_type"`
	Sign             string           `json:"sign"`
	Rank             int              `json:"rank"`
	Level            int              `json:"level"`
	Jointime         int              `json:"jointime"`
	Moral            int              `json:"moral"`
	Silence          int              `json:"silence"`
	Coins            int              `json:"coins"`
	Fans_badge       bool             `json:"fans_badge"`
	Fans_medal       fans_medal       `json:"fans_medal"`
	Official         official         `json:"official"`
	Vip              vip              `json:"vip"`
	Pendant          pendant          `json:"pendant"`
	Nameplate        nameplate        `json:"nameplate"`
	User_honour_info user_honour_info `json:"user_honour_info"`
	Is_followed      bool             `json:"is_followed"`
	Top_photo        string           `json:"top_photo"`
	Theme            theme            `json:"theme"`
	Sys_notice       sys_notice       `json:"sys_notice"`
	Live_room        live_room        `json:"live_room"`
	Birthday         string           `json:"birthday"`
	School           school           `json:"school"`
	Profession       profession       `json:"profession"`
	Tags             tags             `json:"tags"`
	Series           series           `json:"series"`
	Is_senior_member int              `json:"is_senior_member"`
	Mcn_info         mcn_info         `json:"mcn_info"`
	Gaia_res_type    int              `json:"gaia_res_type"`
	Gaia_data        gaia_data        `json:"gaia_data"`
	Is_risk          bool             `json:"is_risk"`
	Elec             elec             `json:"elec"`
	Contract         contract         `json:"contract"`
}

type getUserInfo struct {
	Code    int      `json:"code"`
	Message string   `json:"message"`
	Ttl     int      `json:"ttl"`
	Data    userInfo `json:"data"`
}

func GetUserInfo(mid string) (userInfo userInfo) {
	api := "/x/space/acc/info"
	res, _ := http.Get(host + api + "?mid=" + mid)
	if res.StatusCode == 200 {
		body, _ := io.ReadAll(res.Body)
		var getUserInfo getUserInfo
		json.Unmarshal(body, &getUserInfo)
		userInfo = getUserInfo.Data
	}
	return
}

func GetRoomInfo(shortId string) (roomInfo roomInfo) {
	api := "/room/v1/Room/get_info"
	res, _ := http.Get(liveHost + api + "?room_id=" + shortId)
	if res.StatusCode == 200 {
		body, _ := io.ReadAll(res.Body)
		var getRoomInfo getRoomInfo
		json.Unmarshal(body, &getRoomInfo)
		roomInfo = getRoomInfo.Data
	}
	return
}

func GetRoomLiveVideoUri(roomId string) (realUrl string) {
	api := "/xlive/web-room/v2/index/getRoomPlayInfo"
	param := map[string]string{
		"platform": "h5",
		"protocol": "0",
		"format":   "0,1,2",
		"codec":    "0",
		"qn":       "10000",
		"room_id":  roomId,
	}
	result := u.GetRequest(liveHost+api, param)
	temp := gjson.Get(result, "data.playurl_info.playurl.stream.0.format.0.codec.0").String()
	baseUrl := gjson.Get(temp, "base_url").String()
	host := gjson.Get(temp, "url_info.0.host").String()
	extra := gjson.Get(temp, "url_info.0.extra").String()
	realUrl = host + baseUrl + extra
	return
}

func ListenLiveRoom(roomId string, uid string) {
	fmt.Println("开始监听房间", roomId)
	go downloadFile(roomId)
	c := client.NewClient(roomId)
	c.OnLive(func(l *message.Live) {
		go downloadFile(roomId)
	})
	c.OnDanmaku(func(danmaku *message.Danmaku) {
		if danmaku.Type == message.EmoticonDanmaku {
			// fmt.Printf("[弹幕表情] %s：表情URL： %s\n", danmaku.Sender.Uname, danmaku.Emoticon.Url)
		} else {
			if strconv.Itoa(danmaku.Sender.Uid) == uid {
				fmt.Println("特殊用户弹幕", danmaku.Sender.Uname, ":", danmaku.Content)
				log.Info("特殊用户弹幕", danmaku.Sender.Uname, ":", danmaku.Content)
				reg, _ := regexp.Compile(`\[cut\]\[([0-9-]+)\]\[(.+)\]`)
				found := reg.FindStringSubmatch(danmaku.Content)
				foundLen := len(found)
				var s int64 = 0
				cut := false
				name := time.Now().Format("2006_01_02_03_04_05")
				if foundLen > 1 {
					cut = true
					s, _ = strconv.ParseInt(found[1], 10, 64)
				}
				if foundLen > 2 {
					name = found[2]
				}
				if cut {
					cutRecordFile(roomId, name, int(s))
				}

			}
		}
	})
	log.Info("开始监听房间", roomId)
	err := c.Start()
	if err != nil {
		log.Fatal(err)
	}
	for {
	}
}

func downloadFile(roomId string) {
	roomInfo := GetRoomInfo(roomId)
	if roomInfo.Live_status == 1 {
		videoUri := GetRoomLiveVideoUri(strconv.Itoa(roomInfo.Room_id))
		log.Info("连接直播间", roomId, "视频流")
		fileName, path := getPathAndName(roomId)
		u.Down(videoUri, path, fileName+".flv", time.Hour, func(err error) {
			log.Info("关闭", roomId, "视频流连接")
		})
	}
}

func cutRecordFile(roomId string, outName string, time int) {
	s := time * 60 * 1000000
	fileName, path := getPathAndName(roomId)
	videoInfo, _ := ffmpeg.GetVideoInfo(path + "/" + fileName + ".flv")
	tmpVideoTime, _ := strconv.ParseFloat(videoInfo.Format.Duration, 64)
	tmpVideoTime = tmpVideoTime * 1000000
	videoTime := int(tmpVideoTime)
	if videoTime < s {
		s = videoTime
	}
	startTime := videoTime - s
	fmt.Println("裁切录播：", path+"/cut/"+outName+".flv", "开始")
	log.Info("裁切录播：", path+"/cut/"+outName+".flv", "开始")
	u.MakeDir(path + "/cut/")
	err := ffmpeg.CutVideo(path+"/"+fileName+".flv", path+"/cut/"+outName+".mp4", strconv.Itoa(startTime/1000000), strconv.Itoa(s/1000000))
	if err {
		log.Info("裁切录播：", path+"/cut/"+outName+".flv", "完成")
		fmt.Println("裁切录播：", path+"/cut/"+outName+".flv", "完成")
	}
}

func getPathAndName(roomId string) (fileName string, path string) {
	roomInfo := GetRoomInfo(roomId)
	basePath := "./download/" + roomId
	roomTitleInfo := strconv.Itoa(roomInfo.Uid) + "_" + roomInfo.Title + "_" + time.Now().Format("2006_01_02")
	roomPath := "/" + roomTitleInfo
	fileName = roomTitleInfo
	path = basePath + roomPath
	return
}
